home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
MacWorld 1998 September
/
Macworld (1998-09).dmg
/
Shareware World
/
Info
/
For Developers
/
MacZoop 1.8.3
/
More Classes
/
File Classes
/
ZFile.cpp
< prev
next >
Wrap
Text File
|
1998-07-06
|
22KB
|
876 lines
/*************************************************************************************************
*
*
* MacZoop - "the framework for the rest of us"
*
*
*
* ZFile.cpp -- a generic file object
*
*
*
*
*
* © 1996, Graham Cox
*
*
*
*
*************************************************************************************************/
#include "ZFile.h"
#include "MacZoop.h"
#include "ProjectSettings.h"
#if _CUSTOM_ICON_SUPPORT
#include "PixMapUtils.h"
#include "ZGWorld.h"
#include <icons.h>
#endif
#include <finder.h>
#include <folders.h>
/*-------------------------------*** CONSTRUCTOR ***----------------------------------*/
ZFile::ZFile( const FSSpec& aSpec )
: ZComrade()
{
classID = CLASS_ZFile;
itsSpec = aSpec;
InitFile();
}
/*-------------------------------*** CONSTRUCTOR ***----------------------------------*/
ZFile::ZFile( Str255 fName )
: ZComrade()
{
short vRefNum;
classID = CLASS_ZFile;
FailOSErr( GetVol( NULL, &vRefNum ));
(void) FSMakeFSSpec( vRefNum, 0, fName, &itsSpec );
InitFile();
}
ZFile::ZFile()
: ZComrade()
{
classID = CLASS_ZFile;
itsSpec.vRefNum = 0;
itsSpec.parID = 0;
itsSpec.name[0] = 0;
}
/*-------------------------------*** DESTRUCTOR ***----------------------------------*/
ZFile::~ZFile()
{
Close();
CloseResFork();
}
/*---------------------------------*** INITFILE ***----------------------------------*/
/*
common construction
---------------------------------------------------------------------------------------*/
void ZFile::InitFile()
{
refNum = _NOT_OPEN;
resRefNum = _NOT_OPEN;
isSafeSave = FALSE;
itsType = kUnknownType;
itsCreator = gAppSignature;
ssFSpec = itsSpec;
// if the file already exists, get the file type
// from the file. If file not found, its type is not yet defined.
FInfo finderInfo;
if( FSpGetFInfo( &itsSpec, &finderInfo ) == noErr )
itsType = finderInfo.fdType;
GetDateTime((unsigned long*) &qd.randSeed );
GetTempFolderID();
}
/*------------------------------------*** OPEN ***-----------------------------------*/
/*
Open the file ready for reading or writing
---------------------------------------------------------------------------------------*/
void ZFile::Open()
{
FailOSErr( FSpOpenDF( &itsSpec, fsCurPerm, &refNum ));
SetMark( 0 );
}
/*----------------------------------*** OPENSAFE ***---------------------------------*/
/*
Open the file ready for writing via a safe-save temp file
---------------------------------------------------------------------------------------*/
void ZFile::OpenSafe()
{
// the file is to opened for a safe save. This creates a temporary file in the system's
// temp folder, but with a name built from part of the original plus a random number.
// The file is set to be invisible in the Finder. When the file is later closed, the
// catalogue entries will be swapped and the temp file deleted.
long tfRand;
Str31 tfName;
ssFSpec = itsSpec;
// if the volume of the target and temp folder differ, do a safe save in the
// target's location, otherwise in the temp items folder. This is because ExchangeFiles
// only works on the same volume.
if ( itsSpec.vRefNum == tempFVolID )
ssFSpec.parID = tempFID;
tfRand = ((unsigned long) Random() << 16L ) + Random();
NumToString( ABS( tfRand ), tfName );
// use up to the first 10 chars of the true name, then append our random characters
ssFSpec.name[0] = MIN( ssFSpec.name[0], 10 );
ConcatPStrings( ssFSpec.name, tfName );
// create and open the file
FailOSErr( FSpCreate( &ssFSpec, gAppSignature, 'temp', smSystemScript ));
FailOSErr( FSpOpenDF( &ssFSpec, fsCurPerm, &refNum ));
SetMark( 0 );
isSafeSave = TRUE;
}
/*------------------------------------*** CLOSE ***----------------------------------*/
/*
closes the file, if open
---------------------------------------------------------------------------------------*/
void ZFile::Close()
{
if (( refNum != _NOT_OPEN ) &&
( refNum != 0 ))
FailOSErr( FSClose( refNum ));
refNum = _NOT_OPEN;
// if we are closing after a safe-save, swap the files and delete the temp file, AND,
// if the original file doesn't exist, create it before swapping it.
if ( isSafeSave )
{
isSafeSave = FALSE;
if (! IsReal())
Create();
// n.b. finder info not swapped, so get that from the temp file and apply to the
// real file:
FInfo fi;
FailOSErr( FSpGetFInfo( &ssFSpec, &fi ));
FailOSErr( FSpExchangeFiles( &itsSpec, &ssFSpec ));
FailOSErr( FSpDelete( &ssFSpec ));
fi.fdFlags &= ~fInvisible;
FailOSErr( FSpSetFInfo( &itsSpec, &fi ));
}
}
/*-----------------------------------*** CREATE ***----------------------------------*/
/*
create the file on disk. This does not open it.
---------------------------------------------------------------------------------------*/
void ZFile::Create()
{
FailOSErr( FSpCreate( &itsSpec, itsCreator, itsType, smSystemScript ));
}
/*-----------------------------------*** DISCARD ***---------------------------------*/
/*
deletes the file after closing it if necessary
---------------------------------------------------------------------------------------*/
void ZFile::Discard()
{
if (refNum != _NOT_OPEN )
Close();
FailOSErr( FSpDelete( &itsSpec ));
}
/*------------------------------*** OPENRESFORK ***----------------------------------*/
/*
opens the files's resource fork
---------------------------------------------------------------------------------------*/
void ZFile::OpenResFork()
{
if ( resRefNum == _NOT_OPEN )
{
if ( isSafeSave )
resRefNum = FSpOpenResFile( &ssFSpec, fsCurPerm );
else
resRefNum = FSpOpenResFile( &itsSpec, fsCurPerm );
FailOSErr( ResError());
FailOSErr((resRefNum == -1)? fnfErr : noErr );
}
UseResFile( resRefNum );
}
/*-----------------------------*** CLOSERESFORK ***----------------------------------*/
/*
Closes the resource fork, if open
---------------------------------------------------------------------------------------*/
void ZFile::CloseResFork()
{
if ( resRefNum != _NOT_OPEN )
{
CloseResFile( resRefNum );
FailOSErr( ResError());
}
resRefNum = _NOT_OPEN;
}
/*----------------------------*** CREATERESFORK ***----------------------------------*/
/*
Creates the resource fork, but does not open it
---------------------------------------------------------------------------------------*/
void ZFile::CreateResFork()
{
if ( ! HasResFork())
{
if ( isSafeSave )
FSpCreateResFile( &ssFSpec, gAppSignature, itsType, smSystemScript );
else
FSpCreateResFile( &itsSpec, gAppSignature, itsType, smSystemScript );
FailOSErr( ResError());
}
}
/*----------------------------------*** READ ***-------------------------------------*/
/*
Read some data (<howMuch> bytes, from current mark) from the file into a buffer
---------------------------------------------------------------------------------------*/
void ZFile::Read( Ptr inBuffer, long* howMuch )
{
FailOSErr( isSafeSave? permErr : noErr );
FailOSErr( FSRead( refNum, howMuch, inBuffer ));
}
/*---------------------------------*** WRITE ***-------------------------------------*/
/*
Write data from the buffer to the file, <howMuch> bytes written from current mark.
---------------------------------------------------------------------------------------*/
void ZFile::Write( Ptr outBuffer, long* howMuch )
{
FailOSErr( FSWrite( refNum, howMuch, outBuffer ));
}
/*---------------------------------*** READ ***--------------------------------------*/
/*
Reads the entire file into a Handle. The handle is resized initially to the size of the
file. This technique is unsuitable for large files.
---------------------------------------------------------------------------------------*/
void ZFile::Read( Handle aHandle )
{
long howMuch;
char hs;
FailNIL( aHandle );
FailOSErr( isSafeSave? permErr : noErr );
// the handle is sized to the size of the rest of the data in the file, that is, the
// length less the current position. Override this for different behaviour.
howMuch = GetLength() - GetMark();
SetHandleSize( aHandle, howMuch );
FailOSErr( MemError());
hs = HGetState( aHandle );
HLock( aHandle );
Read( *aHandle, &howMuch );
HSetState( aHandle, hs );
}
/*---------------------------------*** WRITE ***-------------------------------------*/
/*
Writes a handle to the file from the current mark.
---------------------------------------------------------------------------------------*/
void ZFile::Write( Handle aHandle )
{
long howMuch;
char hs;
FailNIL( aHandle );
howMuch = GetHandleSize( aHandle );
hs = HGetState( aHandle );
HLock( aHandle );
Write( *aHandle, &howMuch );
HSetState( aHandle, hs );
// set the length of the file to the current mark, in case the file was
// shortened. This is OK even if multiple calls are made to this method.
SetLength( GetMark());
}
/*---------------------------------*** SETMARK ***-----------------------------------*/
/*
move the file's mark to the absolute position <aMark>. Mark is set to 0 on Open.
---------------------------------------------------------------------------------------*/
void ZFile::SetMark( const long aMark )
{
FailOSErr( SetFPos( refNum, fsFromStart, aMark ));
}
/*--------------------------------*** SETTYPE ***------------------------------------*/
/*
set the type of the file as used in subsequent Create() calls, or if the file already
exists, this changes the file's type on disk. Use with care, since the content of a file
may be totally invalidated by this call.
---------------------------------------------------------------------------------------*/
void ZFile::SetType( const OSType aType )
{
if (aType != itsType)
{
itsType = aType;
if ( IsReal())
{
FInfo fi;
FailOSErr( FSpGetFInfo( &itsSpec, &fi ));
fi.fdType = itsType;
FailOSErr( FSpSetFInfo( &itsSpec, &fi ));
}
}
}
/*-------------------------------*** SETCREATOR ***----------------------------------*/
/*
set the creator of the file as used in subsequent Create() calls, or if the file already
exists, this changes the file's creator on disk.
---------------------------------------------------------------------------------------*/
void ZFile::SetCreator( const OSType aCreator )
{
if ( aCreator != itsCreator )
{
itsCreator = aCreator;
if ( IsReal())
{
FInfo fi;
FailOSErr( FSpGetFInfo( &itsSpec, &fi ));
fi.fdCreator = itsCreator;
FailOSErr( FSpSetFInfo( &itsSpec, &fi ));
}
}
}
/*--------------------------------*** GETMARK ***--------------=---------------------*/
/*
Get the current position of the file
---------------------------------------------------------------------------------------*/
long ZFile::GetMark()
{
long mark = 0;
FailOSErr( GetFPos( refNum, &mark ));
return mark;
}
/*--------------------------------*** GETFSSPEC ***----------------------------------*/
/*
get the file specification record of the file. You can get the name from this.
---------------------------------------------------------------------------------------*/
void ZFile::GetFSSpec( FSSpec* aSpec )
{
*aSpec = itsSpec;
}
/*-------------------------------*** SETRESFORK ***----------------------------------*/
/*
sets the resfork of this file to be the current resource file, and returns the one set
originally. This will only work if the res fork exists and is open.
---------------------------------------------------------------------------------------*/
void ZFile::SetResFork(short* curRes)
{
*curRes = CurResFile();
if ( resRefNum != _NOT_OPEN )
UseResFile( resRefNum );
}
/*-------------------------------*** GETLENGTH ***-----------------------------------*/
/*
Get the length of the file.
---------------------------------------------------------------------------------------*/
long ZFile::GetLength()
{
long len = 0;
FailOSErr( GetEOF( refNum, &len ));
return len;
}
/*--------------------------------*** SETLENGTH ***----------------------------------*/
/*
set the length of the file. You should call this after updating a file in case it got
shorter. Write() with a handle argument does this automatically.
---------------------------------------------------------------------------------------*/
void ZFile::SetLength( const long aLength )
{
FailOSErr( SetEOF( refNum, aLength ));
}
/*--------------------------------*** GETTYPE ***------------------------------------*/
/*
get the file's type
---------------------------------------------------------------------------------------*/
OSType ZFile::GetType()
{
return itsType;
}
/*--------------------------------*** GETINFO ***------------------------------------*/
/*
get finder info about the file on disk
---------------------------------------------------------------------------------------*/
void ZFile::GetInfo( FInfo* fi )
{
if ( isSafeSave )
FailOSErr( FSpGetFInfo( &ssFSpec, fi ));
else
FailOSErr( FSpGetFInfo( &itsSpec, fi ));
}
/*--------------------------------*** SETINFO ***------------------------------------*/
/*
set the finder info about the file on disk
---------------------------------------------------------------------------------------*/
void ZFile::SetInfo( FInfo* fi )
{
if ( isSafeSave )
FailOSErr( FSpSetFInfo( &ssFSpec, fi ));
else
FailOSErr( FSpSetFInfo( &itsSpec, fi ));
}
/*-----------------------------*** HASRESFORK ***------------------------------------*/
/*
does this file have a resource fork? (n.b. if file not created yet, returns false)
---------------------------------------------------------------------------------------*/
Boolean ZFile::HasResFork()
{
Str31 fName;
CInfoPBRec pb;
OSErr theErr;
if ( isSafeSave )
CopyPString( ssFSpec.name, fName );
else
CopyPString( itsSpec.name, fName );
pb.hFileInfo.ioCompletion = NULL;
pb.hFileInfo.ioNamePtr = fName;
pb.hFileInfo.ioVRefNum = isSafeSave? ssFSpec.vRefNum : itsSpec.vRefNum;
pb.hFileInfo.ioDirID = isSafeSave? ssFSpec.parID : itsSpec.parID;
pb.hFileInfo.ioFDirIndex = 0;
theErr = PBGetCatInfoSync( &pb );
if (theErr)
return FALSE;
else
{
// see if any physical bytes are alloted to the resource fork
return( pb.hFileInfo.ioFlRPyLen > 0 );
}
}
/*-----------------------------*** HASDATAFORK ***-----------------------------------*/
/*
does this file have a data fork? (n.b. if file not created yet, returns false)
---------------------------------------------------------------------------------------*/
Boolean ZFile::HasDataFork()
{
Str31 fName;
CInfoPBRec pb;
OSErr theErr;
if ( isSafeSave )
CopyPString( ssFSpec.name, fName );
else
CopyPString( itsSpec.name, fName );
pb.hFileInfo.ioCompletion = NULL;
pb.hFileInfo.ioNamePtr = fName;
pb.hFileInfo.ioVRefNum = isSafeSave? ssFSpec.vRefNum : itsSpec.vRefNum;
pb.hFileInfo.ioDirID = isSafeSave? ssFSpec.parID : itsSpec.parID;
pb.hFileInfo.ioFDirIndex = 0;
theErr = PBGetCatInfoSync( &pb );
if (theErr)
return FALSE;
else
{
// see if any physical bytes are alloted to the data fork
return( pb.hFileInfo.ioFlPyLen > 0 );
}
}
/*--------------------------------*** ISREAL ***-------------------------------------*/
/*
does this file exist on the physical diskdrive (or network volume)?
---------------------------------------------------------------------------------------*/
Boolean ZFile::IsReal()
{
OSErr theErr;
FInfo fi;
theErr = FSpGetFInfo( &itsSpec, &fi );
return (theErr == noErr);
}
/*-------------------------------*** ISLOCKED ***------------------------------------*/
/*
is this file locked?
---------------------------------------------------------------------------------------*/
Boolean ZFile::IsLocked()
{
OSErr theErr;
FInfo fi;
theErr = FSpGetFInfo( &itsSpec, &fi );
return (( theErr == noErr ) &&
( fi.fdFlags & 0x1000 ));
}
/*---------------------------------*** ISOPEN ***------------------------------------*/
/*
is this file currently open?
---------------------------------------------------------------------------------------*/
Boolean ZFile::IsOpen()
{
return ( refNum != _NOT_OPEN );
}
/*-----------------------------*** MAKECUSTOMICON ***--------------------------------*/
/*
add a custom finder icon to the file, built from the picture, pixmap or GWorld object
passed. This is a one-line solution to this problem, and will do the right thing even
if the resource fork is already open, etc.
---------------------------------------------------------------------------------------*/
void ZFile::MakeCustomIcon( PicHandle srcImage )
{
#if _CUSTOM_ICON_SUPPORT
FailNILParam( srcImage );
Handle icnSuite = ConstructCustomIconSuite( srcImage );
if ( icnSuite )
{
SaveCustomIconSuite( icnSuite );
// dispose of the suite, but not the icon data, since we just added it as a resource,
// and in any case that routine just NULLED the icon data here.
FailOSErr( DisposeIconSuite( icnSuite, FALSE ));
}
#endif
}
/*-----------------------------*** MAKECUSTOMICON ***--------------------------------*/
void ZFile::MakeCustomIcon( ZGWorld* srcImage )
{
#if _CUSTOM_ICON_SUPPORT
FailNILParam( srcImage );
Rect r;
PicHandle p;
SetRect( &r, 0, 0, 32, 32 );
FailNIL( p = srcImage->MakePicture( &r ));
try
{
MakeCustomIcon( p );
}
catch( OSErr err )
{
KillPicture( p );
throw err;
}
KillPicture( p );
#endif
}
/*------------------------*** CONSTRUCTCUSTOMICONSUITE ***---------------------------*/
/*
build an icon suite from the picture data passed
---------------------------------------------------------------------------------------*/
Handle ZFile::ConstructCustomIconSuite( PicHandle srcPic )
{
Handle icnSuite = NULL;
#if _CUSTOM_ICON_SUPPORT
Handle theIcon;
char pHState;
pHState = HGetState((Handle) srcPic );
HNoPurge((Handle) srcPic );
FailOSErr( NewIconSuite( &icnSuite ));
FailNIL( theIcon = MakeIconFromPicture( srcPic, kLarge8BitData ));
FailOSErr( AddIconToSuite( theIcon, icnSuite, kLarge8BitData ));
FailNIL( theIcon = MakeIconFromPicture( srcPic, kLarge4BitData ));
FailOSErr( AddIconToSuite( theIcon, icnSuite, kLarge4BitData ));
FailNIL( theIcon = MakeIconFromPicture( srcPic, kLarge1BitMask ));
FailOSErr( AddIconToSuite( theIcon, icnSuite, kLarge1BitMask ));
FailNIL( theIcon = MakeIconFromPicture( srcPic, kSmall8BitData ));
FailOSErr( AddIconToSuite( theIcon, icnSuite, kSmall8BitData ));
FailNIL( theIcon = MakeIconFromPicture( srcPic, kSmall4BitData ));
FailOSErr( AddIconToSuite( theIcon, icnSuite, kSmall4BitData ));
FailNIL( theIcon = MakeIconFromPicture( srcPic, kSmall1BitMask ));
FailOSErr( AddIconToSuite( theIcon, icnSuite, kSmall1BitMask ));
HSetState((Handle) srcPic, pHState );
#endif
return icnSuite;
}
/*---------------------------*** SAVECUSTOMICONSUITE ***-----------------------------*/
/*
save the icons in the suite to the resource fork as finder icons
---------------------------------------------------------------------------------------*/
void ZFile::SaveCustomIconSuite( Handle icnSuite )
{
#if _CUSTOM_ICON_SUPPORT
if ( icnSuite )
{
Handle iconH, oldH;
short id = kCustomIconResource;
CreateResFork();
OpenResFork();
// remove any existing icons in the file. Normally there won't be any since
// this is usually done under a safe save.
oldH = Get1Resource( kLarge8BitData, id );
if ( oldH )
{
RemoveResource( oldH );
DisposeHandle( oldH );
}
oldH = Get1Resource( kLarge4BitData, id );
if ( oldH )
{
RemoveResource( oldH );
DisposeHandle( oldH );
}
oldH = Get1Resource( kLarge1BitMask, id );
if ( oldH )
{
RemoveResource( oldH );
DisposeHandle( oldH );
}
oldH = Get1Resource( kSmall8BitData, id );
if ( oldH )
{
RemoveResource( oldH );
DisposeHandle( oldH );
}
oldH = Get1Resource( kSmall4BitData, id );
if ( oldH )
{
RemoveResource( oldH );
DisposeHandle( oldH );
}
oldH = Get1Resource( kSmall1BitMask, id );
if ( oldH )
{
RemoveResource( oldH );
DisposeHandle( oldH );
}
// save each icon in the suite
FailOSErr( GetIconFromSuite( &iconH, icnSuite, kLarge8BitData ));
AddResource( iconH, kLarge8BitData, id, "\p" );
FailOSErr( ResError());
AddIconToSuite( NULL, icnSuite, kLarge8BitData );
::WriteResource( iconH );
ReleaseResource( iconH );
FailOSErr( GetIconFromSuite( &iconH, icnSuite, kLarge4BitData ));
AddResource( iconH, kLarge4BitData, id, "\p" );
FailOSErr( ResError());
AddIconToSuite( NULL, icnSuite, kLarge4BitData );
::WriteResource( iconH );
ReleaseResource( iconH );
FailOSErr( GetIconFromSuite( &iconH, icnSuite, kLarge1BitMask ));
AddResource( iconH, kLarge1BitMask, id, "\p" );
FailOSErr( ResError());
AddIconToSuite( NULL, icnSuite, kLarge1BitMask );
::WriteResource( iconH );
ReleaseResource( iconH );
FailOSErr( GetIconFromSuite( &iconH, icnSuite, kSmall8BitData ));
AddResource( iconH, kSmall8BitData, id, "\p" );
FailOSErr( ResError());
AddIconToSuite( NULL, icnSuite, kSmall8BitData );
::WriteResource( iconH );
ReleaseResource( iconH );
FailOSErr( GetIconFromSuite( &iconH, icnSuite, kSmall4BitData ));
AddResource( iconH, kSmall4BitData, id, "\p" );
FailOSErr( ResError());
AddIconToSuite( NULL, icnSuite, kSmall4BitData );
::WriteResource( iconH );
ReleaseResource( iconH );
FailOSErr( GetIconFromSuite( &iconH, icnSuite, kSmall1BitMask ));
AddResource( iconH, kSmall1BitMask, id, "\p" );
FailOSErr( ResError());
AddIconToSuite( NULL, icnSuite, kSmall1BitMask );
::WriteResource( iconH );
ReleaseResource( iconH );
CloseResFork();
// make sure the "custom icon" bit is set in the finder info
FInfo fi;
GetInfo( &fi );
fi.fdFlags |= kHasCustomIcon;
SetInfo( &fi );
}
#endif
}
void ZFile::GetTempFolderID()
{
short fv;
long fd;
FailOSErr( FindFolder( kOnSystemDisk, kTemporaryFolderType, kCreateFolder, &fv, &fd ));
tempFID = fd;
tempFVolID = fv;
}